#ifndef __M_DB_H__
#define __M_DB_H__

#include "util.hpp"
#include <mutex>
#include <cassert>

class user_table
{
public:
    user_table(const std::string &host,
               const std::string &usr,
               const std::string &pwd,
               const std::string &db,
               const uint16_t port = 3306 /* 为0也可，默认设置为3306 */)
    {
        // 初始化mysql操作句柄
        _mysql = mysql_util::mysql_create(host, usr, pwd, db, port);
        assert(_mysql != NULL);
    }
    ~user_table()
    {
        // 释放mysql操作句柄
        mysql_util::mysql_destory(_mysql);
        _mysql = NULL;
    }
    // 注册 新增用户
    bool add(Json::Value &user)
    {
#define ADD_USER "insert user values(null, '%s', password('%s'), 1000, 0, 0);" /* 使用内置函数password()对密码做处理——密码加密后再存储， \
 默认分数为1000，默认场次为0 */

        // 检查用户是否存在——可忽略，因为数据库中设置username唯一后，插入相同的username的数据时就会报错
        // Json::Value val;
        // bool ret = select_by_name(user["username"].asCString(), val);
        // if (ret == true)
        // {
        //     DBG_LOG("%s has existed!", user["username"].asCString());
        //     return false;
        // }
        if(user["username"].isNull() || user["password"].isNull())
        {
            DBG_LOG("enter both username and password!");
            return false;
        }
        // 添加用户
        char sql[4096] = {0};
        sprintf(sql, ADD_USER, user["username"].asCString(), user["password"].asCString());
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            ERR_LOG("add user failed!");
            return false;
        }

        return true;
    }
    // 登录 验证用户并返回详细的用户信息
    bool login(Json::Value &user)
    {
        // 以用户名和密码共同作为查询过滤条件，查询到则表示存在用户且用户名密码一致，否则表示用户名或密码错误
        // Json::Value db_user;
        // bool ret = select_by_name(user["username"].asCString(), db_user);
        // if(ret == false)
        // {
        //     DBG_LOG("用户不存在！");
        //     return false;
        // }
        // if(user["password"].asCString() != db_user["password"].asCString())
        // {
        //     DBG_LOG("密码错误！");
        //     return false;
        // }
        // user["id"] = db_user["id"];
        // user["score"] = db_user["score"];
        // user["total_count"] = db_user["total_count"];
        // user["win_count"] = db_user["win_count"];
        // return true;

        if(user["username"].isNull() || user["password"].isNull())
        {
            DBG_LOG("enter both username and password!");
            return false;
        }

        // 查找数据
#define LOGIN_USER "select id, score, total_count, win_count from user where username='%s' and password=password('%s');"
        char sql[4096] = {0};
        sprintf(sql, LOGIN_USER, user["username"].asCString(), user["password"].asCString());

        MYSQL_RES *result = NULL;
        {
            // 内存中存在查询和保存两个原子操作，单独的每个都是线程安全的，但是在一起就不是线程安全的，所以对这整体两个操作要加互斥锁
            std::unique_lock<std::mutex> lock(_mutex);

            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                DBG_LOG("user login failed!");
                return false;
            }
            // 保存查找到的数据到本地
            result = mysql_store_result(_mysql);
            // 没有查找到数据，即不存在该用户
            if (result == NULL)
            {
                DBG_LOG("not have login_user info!");
                return false;
            }
        }
        // 获取结果集中的条数 0 or 1
        uint64_t row_num = mysql_num_rows(result);
        if (row_num != 1)
        {
            mysql_free_result(result);

            // ERR_LOG("the login_user is not unique!");
            ERR_LOG("the login_user not exists or password error!");
            return false;
        }
        // 遍历结果集
        MYSQL_ROW row = mysql_fetch_row(result);
        user["id"] = (Json::UInt)std::stol(row[0]);// Json::Value不支持long类型的强转，因此选择使用Json::Value自己的类型UInt
        user["score"] = std::stoi(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);

        // 释放结果集
        mysql_free_result(result);

        return true;
    }
    // 通过用户名获取用户信息
    bool select_by_name(const std::string &name, Json::Value &user)
    {
#define SELECT_BY_NAME "select id, score, total_count, win_count from user where username='%s';"
        char sql[4096] = {0};
        sprintf(sql, SELECT_BY_NAME, name.c_str());

        MYSQL_RES *result = NULL;
        {
            std::unique_lock<std::mutex> lock(_mutex);

            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                DBG_LOG("get user by name failed!");
                return false;
            }
            // 保存查找到的数据到本地
            result = mysql_store_result(_mysql);
            // 没有查找到数据，即不存在该用户
            if (result == NULL)
            {
                DBG_LOG("the user not exists!");
                return false;
            }
        }

        // 获取结果集中的条数 0 or 1
        uint64_t row_num = mysql_num_rows(result);
        if (row_num != 1)
        {
            mysql_free_result(result);

            // ERR_LOG("the login_user is not unique!");
            ERR_LOG("the login_user not exists or password error!");
            return false;
        }
        // 遍历结果集
        MYSQL_ROW row = mysql_fetch_row(result);
        user["id"] = (Json::UInt)std::stol(row[0]);
        user["username"] = name;
        user["score"] = std::stoi(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);

        // 释放结果集
        mysql_free_result(result);

        return true;
    }
    // 通过用户id获取用户信息
    bool select_by_id(const uint64_t id, Json::Value &user)
    {
#define SELECT_BY_ID "select username, score, total_count, win_count from user where id=%ld;"
        char sql[4096] = {0};
        sprintf(sql, SELECT_BY_ID, id);

        MYSQL_RES *result = NULL;
        {
            std::unique_lock<std::mutex> lock(_mutex);

            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                DBG_LOG("get user by id failed!");
                return false;
            }
            // 保存查找到的数据到本地
            result = mysql_store_result(_mysql);
            // 没有查找到数据，即不存在该用户
            if (result == NULL)
            {
                DBG_LOG("the user not exists!");
                return false;
            }
        }

        // 获取结果集中的条数 0 or 1
        uint64_t row_num = mysql_num_rows(result);
        if (row_num != 1)
        {
            mysql_free_result(result);

            // ERR_LOG("the login_user is not unique!");
            ERR_LOG("the login_user not exists or password error!");
            return false;
        }
        // 遍历结果集
        MYSQL_ROW row = mysql_fetch_row(result);
        user["id"] = (Json::UInt)id;
        user["username"] = row[0];
        user["score"] = std::stoi(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);

        // 释放结果集
        mysql_free_result(result);

        return true;
    }
    // 胜利，增加天梯分数（每场次+10），增加对战场次，增加胜利场次
    bool win(const uint64_t id)
    {
#define SCORE_WIN "update user set score=score+10, total_count=total_count+1, win_count=win_count+1 where id=%ld;"
        char sql[4096] = {0};
        sprintf(sql, SCORE_WIN, id);
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            DBG_LOG("update win set failed!");
            return false;
        }
        return true;
    }
    // 失败，减少天梯分数（每场次-10），增加对战场次
    bool lose(const uint64_t id)
    {
#define SCORE_LOSE "update user set score=score-10, total_count=total_count+1 where id=%ld;"
        char sql[4096] = {0};
        sprintf(sql, SCORE_LOSE, id);
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            DBG_LOG("update lose set failed!");
            return false;
        }
        return true;
    }

private:
    MYSQL *_mysql;     // mysql操作句柄
    std::mutex _mutex; // 互斥锁 ，保护多线程下的数据安全访问
};

#endif